﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using System.Web.UI;
using NativeCode.Core.Data;
using NativeCode.Core.Extensions;
using NativeCode.Web.Controls.Data;
using NativeCode.Web.Controls.Renderers;
using NativeCode.Web.Extensions;

namespace NativeCode.Web.Controls.Filters
{
	[ControlBuilder(typeof (GenericControlBuilder))]
	public class QueryFilterBar : QueryFilterBar<object>
	{
	}

	public class QueryFilterBar<TDataItem> : GenericDataBoundControl<TDataItem, FilterBarQuery>, IPostBackEventHandler
		where TDataItem : new()
	{
		private readonly List<QueryFilterBarItem> _filterBarItems = new List<QueryFilterBarItem>();

		public QueryFilterBar()
		{
			AvailableFilters = new List<QueryFilter>(FilterableTypesRegistry.GetFiltersFor(typeof(TDataItem)));
			LabelFormat = "Find any {0} where:";
			RenderOptions = new QbeFilterBarRenderOptions();
		}

		#region Events

		public EventHandler<EventArgs> OnFiltersChanged = delegate { };
		public EventHandler<EventArgs> OnFiltersCleared = delegate { };

		#region Event Helpers

		protected void DoEventFiltersCleared()
		{
			EventHandler<EventArgs> handler = OnFiltersCleared;
			if (handler != null)
			{
				handler(this, EventArgs.Empty);
			}
		}

		protected void DoEventFiltersChanged()
		{
			EventHandler<EventArgs> handler = OnFiltersChanged;
			if (handler != null)
			{
				handler(this, EventArgs.Empty);
			}
		}

		#endregion

		#endregion

		#region Properties

		private string _typeAsName;

		protected string TypeAsName
		{
			get { return _typeAsName ?? (_typeAsName = typeof (TDataItem).Name); }
		}

		protected QbeFilterBarRenderOptions RenderOptions { get; private set; }

		public List<QueryFilter> AvailableFilters { get; private set; }

		public List<QueryFilterBarItem> FilterBarItems
		{
			get { return _filterBarItems; }
		}

		public string Label
		{
			get { return !string.IsNullOrEmpty(LabelFormat) ? string.Format(LabelFormat, typeof (TDataItem).Name) : typeof (TDataItem).Name; }
		}

		public string LabelFormat { get; set; }

		#endregion

		#region Helpers

		public Dictionary<string, string> GetAvailableFiltersAsDictionary()
		{
			return AvailableFilters
				.Select(av => av.PropertyName).Distinct()
				.OrderBy(property => property)
				.ToDictionary(name => name, value => value.ToDisplayText());
		}

		public string SerializeFilterBarItems()
		{
			return FilterBarItems.ToXmlString();
		}

		public List<QueryFilterBarItem> DeserializeFilterBarItems(string data)
		{
			if (data.IsNullOrEmpty())
			{
				return data.FromXmlString<List<QueryFilterBarItem>>();
			}

			return new List<QueryFilterBarItem>();
		}

		#endregion

		#region Filtering Helpers

		public IQueryable<TDataItem> ApplyFilterToQueryable(IQueryable<TDataItem> query)
		{
			return ApplyFilter(query);
		}

		public IQueryable<TDataItem> ApplyFilter(IQueryable<TDataItem> query)
		{
			Expression<Func<TDataItem, bool>> predicate = null;

			foreach (QueryFilterBarItem barItem in FilterBarItems)
			{
				QueryFilter filter = FilterableTypesRegistry.GetFilterByName(barItem.FilterKey);
				if (filter != null)
				{
					predicate = filter.ApplyFilter(predicate, barItem.FilterValue, barItem.FilterConjunction);
				}
			}

			return (predicate != null) ? query.Where(predicate) : query;
		}

		#endregion

		#region Control Overrides

		public override void PerformDataBinding(IQueryable<TDataItem> results, int resultsCount)
		{
			// Don't data-bind anything. Pretend like we did though.
			ViewState.Write(UniqueID, FilterBarItems.ToList());
		}

		protected override void CreateChildControls()
		{
			if (Page.IsPostBack)
			{
				List<QueryFilterBarItem> filterBarItems = ViewState.Read<List<QueryFilterBarItem>>(UniqueID);
				if (filterBarItems != null && filterBarItems.Count > 0)
				{
					FilterBarItems.Clear();
					FilterBarItems.AddRange(filterBarItems);
				}
			}

			ChildControlsCreated = true;
		}

		protected override void Render(HtmlTextWriter writer)
		{
			QueryFilterBarRenderer<TDataItem> renderer = new QueryFilterBarRenderer<TDataItem>(writer, this, RenderOptions);
			renderer.RenderFilterBarControl();
		}

		#endregion

		#region Implementation of IPostBackEventHandler

		public void RaisePostBackEvent(string eventArgument)
		{
			switch (eventArgument)
			{
				case PostBackEvents.FiltersCleared:
					FilterBarItems.Clear();
					DoEventFiltersCleared();
					break;
				case PostBackEvents.FiltersChanged:
					DoEventFiltersChanged();
					break;
			}
		}

		#endregion

		#region Nested type: PostBackEvents

		public static class PostBackEvents
		{
			public const string FiltersChanged = "FiltersChanged";
			public const string FiltersCleared = "FiltersCleared";
		}

		#endregion
	}

	[DataContract]
	[Serializable]
	public class QbeFilterBarRenderOptions
	{
		public QbeFilterBarRenderOptions()
		{
			// 0 = auto format the columns based on number of items.
			NumberOfColumns = 1;
		}

		[DataMember]
		public int NumberOfColumns { get; set; }
	}
}